package org.itec.yeast.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import org.itec.yeast.trdo.TRAllele;
import org.itec.yeast.trdo.TRAlleleSQL;
import org.itec.yeast.trdo.TRInteraction;
import org.itec.yeast.trdo.TRInteractionSQL;
import org.itec.yeast.trdo.TRSmf;
import org.itec.yeast.trdo.TRSmfSQL;
import org.itec.yeast.trdo.TRStrain;
import org.itec.yeast.trdo.TRStrainSQL;
import org.itec.yeast.trdo.TRYeastGeneSQL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DataFetcher {
	private static final Logger log = LoggerFactory.getLogger(DataFetcher.class);
	private static final String FIELD_SEPARATER = "\t";
	private static final String MUTANT_SEPARATER = "_";
	private static final String CONDITION_FAKE = "T30";
	private static final int FIELD_NUMBER = 12;
	private static final int STORE_NUM_MAX = 1000;
	private static final int STRAIN_MARKER_TYPE_Q = 0;
	private static final int STRAIN_MARKER_TYPE_A = 1;
	
	/*
	 * 一条SGA记录共有12列数据
	 */
	private static enum SGAColums {
		COL_QUERY_STRAIN, COL_ARRAY_STRAIN, COL_E_SCORE, COL_E_SCORE_STD, 
		COL_P_VALUE, COL_QUERY_SMF, COL_QUERY_SMF_STD, COL_ARRAY_SMF, 
		COL_ARRAY_SMF_STD, COL_EXPECTED_DMF, COL_OBSERVED_DMF, COL_OBSERVED_DMF_STD
	};
	
	private String PREFIX_PATH = "";
	private String DATA_FILEPATH = "";
	private String ARRAY_DATA_FILEPATH = "";
	private String QUERY_DATA_FILEPATH = "";
	
	/*
	 * 所有Gene的名称和ID，这个ID是表GENE的主键，为其他表提供外键
	 * Map的key是Gene的名称，value是ID
	 */
	private Map<String, Integer> geneIDs = new HashMap<String, Integer>();
	
	private List<TRAllele> alleles = new ArrayList<TRAllele>();
	private List<TRStrain> queryStrains = new ArrayList<TRStrain>();
	private List<TRStrain> arrayStrains = new ArrayList<TRStrain>();
	private List<TRSmf> smfs = new ArrayList<TRSmf>();
	private List<TRInteraction> interactions = new ArrayList<TRInteraction>();
	
	private List<String> storedAlleles = new ArrayList<String>();
	private List<String> storedQStrains = new ArrayList<String>();
	private List<String> storedAStrains = new ArrayList<String>();
	
	private List<String> alleleNames = new ArrayList<String>();
	private List<String> queryNames = new ArrayList<String>();
	private List<String> arrayNames = new ArrayList<String>();
	private List<String> smfNames = new ArrayList<String>();
	
	/**
	 * 构造方法，初始化配置信息
	 * @throws URISyntaxException
	 */
	public DataFetcher() throws URISyntaxException{
		super();
		String fullPath = DataFetcher.class.getClassLoader().getResource("").toURI().getPath();
		PREFIX_PATH = fullPath.substring(0, fullPath.indexOf("WEB-INF/classes") - 1);
		ResourceBundle bundle = ResourceBundle.getBundle("yeast");
		DATA_FILEPATH = bundle.getString("SGA_Path");
		ARRAY_DATA_FILEPATH = bundle.getString("CORRELATION_ARRAY");
		QUERY_DATA_FILEPATH = bundle.getString("CORRELATION_QUERY");
		if (DATA_FILEPATH.equals("") || QUERY_DATA_FILEPATH.equals("") || ARRAY_DATA_FILEPATH.equals("") || PREFIX_PATH.equals("")) {
			log.error("Correlation数据信息配置错误，请检查yeast.properties文件!");
			return;
		}
		
		TRYeastGeneSQL yeastSql = new TRYeastGeneSQL();
		TRAlleleSQL alleleSql = new TRAlleleSQL();
		TRStrainSQL strainSql = new TRStrainSQL();
		
		this.geneIDs = yeastSql.getGeneIDs();
		if (this.geneIDs.size() == 0){
			log.debug("表GENE中没有数据。请先获取Gene信息");
			return;
		}
		this.storedAlleles = alleleSql.getStoredAlleles();
		this.storedQStrains = strainSql.getStoredStrains(STRAIN_MARKER_TYPE_Q);
		this.storedAStrains = strainSql.getStoredStrains(STRAIN_MARKER_TYPE_A);
	}
	
	private void storeCorrelationAllele(String line, int field){
		TRAlleleSQL alleleSQL = new TRAlleleSQL();
		String[] content = line.split(FIELD_SEPARATER);
		String alleleFullName = content[field];
		String alleleName = "";
		String mutantInfo = null;
		String mutantType = null;
		if (alleleFullName.contains(MUTANT_SEPARATER)){
			alleleName = alleleFullName.substring(0, alleleFullName.indexOf(MUTANT_SEPARATER));
			mutantInfo = alleleFullName.substring(alleleFullName.indexOf(MUTANT_SEPARATER) + 1);
			// mutant type是mutant info的一部分，但是现在还没有确定格式，因此现在认为是一样的
			mutantType = mutantInfo;
		} else {
			alleleName = alleleFullName;
		}
		
		if (isAlleleStored(alleleFullName) || this.storedAlleles.contains(alleleName))
			return;
		alleleNames.add(alleleFullName);

		TRAllele allele = null;
		int geneID = -1;
		if (geneIDs.containsKey(alleleName)){
			geneID = geneIDs.get(alleleName);
		} else {
			return;
		}
		allele = new TRAllele(geneID, alleleName);
		allele.setMutantInfo(mutantInfo);
		allele.setMutantType(mutantType);
		alleles.add(allele);
		
		if (alleles.size() >= STORE_NUM_MAX){
			alleleSQL.insert(alleles);
			alleles.clear();
		}
	}
	
	private void storeInteractionAllele(String line){
		TRAlleleSQL alleleSQL = new TRAlleleSQL();
		String alleleFullName = line.split(FIELD_SEPARATER)[0];
		String alleleName = "";
		if (alleleFullName.contains(MUTANT_SEPARATER)){
			alleleName = alleleFullName.substring(0, alleleFullName.indexOf(MUTANT_SEPARATER));
		} else {
			alleleName = alleleFullName;
		}
		
		if (isAlleleStored(alleleFullName) || this.storedAlleles.contains(alleleName))
			return;
		alleleNames.add(alleleFullName);
		TRAllele allele = getAlleleData(line);
		alleles.add(allele);
		if (alleles.size() >= STORE_NUM_MAX){
			alleleSQL.insert(alleles);
			alleles.clear();
		}
	}
	
	private void storeStrains(String line, int type){
		TRStrainSQL strainSQL = new TRStrainSQL();
		TRAlleleSQL alleleSQL = new TRAlleleSQL();
		String strainName = line.split(FIELD_SEPARATER)[type];
		if (type == STRAIN_MARKER_TYPE_Q){
			if (isQStrainStored(strainName) || storedQStrains.contains(strainName)){
				return;
			} else {
				queryNames.add(strainName);
			}
		} else if (type == STRAIN_MARKER_TYPE_A){
			if (isAStrainStored(strainName) || storedAStrains.contains(strainName)){
				return;
			} else {
				arrayNames.add(strainName);
			}
		} else {
			log.error("Strain类型错误");
			return;
		}

		String alleleName = "";
		if (strainName.contains(MUTANT_SEPARATER)){
			alleleName = strainName.substring(0, strainName.indexOf(MUTANT_SEPARATER));
		} else {
			alleleName = strainName;
		}

		// Gene中不存在相应的Allele信息
		int geneID = -1;
		if (!geneIDs.containsKey(alleleName)){
			return;
		} else {
			geneID = geneIDs.get(alleleName);
		}
		int alleleID = alleleSQL.getAlleleID(alleleName);
		if (alleleID == -1){
			alleleID = geneID;
		}
		TRStrain strain = new TRStrain(alleleID, geneID);
		strain.setStrainName(strainName);
		strain.setStrainType(type);
		
		if (type == STRAIN_MARKER_TYPE_Q){
			queryStrains.add(strain);
			if (queryStrains.size() >= STORE_NUM_MAX){
				strainSQL.insert(queryStrains);
				queryStrains.clear();
			}
		} else if (type == STRAIN_MARKER_TYPE_A){
			arrayStrains.add(strain);
			if (arrayStrains.size() >= STORE_NUM_MAX){
				strainSQL.insert(arrayStrains);
				arrayStrains.clear();
			}
		}
	}
	
	private void storeSmf(String line){
		TRStrainSQL strainSQL = new TRStrainSQL();
		TRSmfSQL smfSQL = new TRSmfSQL();
		String strainFullName = line.split(FIELD_SEPARATER)[STRAIN_MARKER_TYPE_Q];
		String strainName;
		if (strainFullName.contains(MUTANT_SEPARATER)){
			strainName = strainFullName.substring(0, strainFullName.indexOf(MUTANT_SEPARATER));
		} else {
			strainName = strainFullName;
		}
		if (smfNames.contains(strainName)){
			return;
		}
		smfNames.add(strainName);
		int strainID = strainSQL.getStrainID(strainName);
		TRSmf smf = new TRSmf(strainID);
		smf.setScore(Double.parseDouble(line.split(FIELD_SEPARATER)[2]));
		smf.setStandardDeviation(Double.parseDouble(line.split(FIELD_SEPARATER)[3]));
		smf.setCondition(CONDITION_FAKE);
		smfs.add(smf);
		if (smfs.size() >= STORE_NUM_MAX){
			smfSQL.insert(smfs);
			smfs.clear();
		}
		
 	}
	
	private TRAllele getAlleleData(String line){
		String[] content = line.split(FIELD_SEPARATER);
		TRAllele allele = null;
		if (content.length != FIELD_NUMBER){
			log.error("SGA数据错误。本行内容: " + line);
			return null;
		}
		
		String strainName = content[SGAColums.COL_QUERY_STRAIN.ordinal()];
		String alleleName = null;
		String mutantInfo = null;
		String mutantType = null;
		if (strainName.contains(MUTANT_SEPARATER)){
			alleleName = strainName.substring(0, strainName.indexOf(MUTANT_SEPARATER));
			mutantInfo = strainName.substring(strainName.indexOf(MUTANT_SEPARATER) + 1);
			
			// mutant type是mutant info的一部分，但是现在还没有确定格式，因此现在认为是一样的
			mutantType = mutantInfo;
		} else {
			alleleName = strainName;
		}
		
		int geneID = -1;
		if (geneIDs.containsKey(alleleName)){
			geneID = geneIDs.get(alleleName);
		} else {
			return null;
		}
		
		allele = new TRAllele(geneID, alleleName);
		allele.setMutantInfo(mutantInfo);
		allele.setMutantType(mutantType);
		
		return allele;
	}
	
	/**
	 * 
	 * @param line
	 * 			SGA内容
	 * @param type
	 * 			Strain的类型。0代表类型为Query，1代表类型为Array
	 * @return
	 */
	private TRStrain getStrainData(String line, int type){
		String[] content = line.split(FIELD_SEPARATER);
		TRAlleleSQL alleleSQL = new TRAlleleSQL();
		TRStrain strain = null;
		if (content.length != 12){
			log.error("SGA数据错误。本行内容: " + line);
			return null;
		}
		
		String strainName = content[type];
		int alleleID = alleleSQL.getAlleleID(strainName);
		int geneID = geneIDs.get(strainName);
		strain = new TRStrain(alleleID, geneID);
		strain.setStrainName(strainName);
		strain.setStrainType(type);
		return strain;
	}
	
	private boolean isAlleleStored(String alleleFullName){
		if (alleleNames.contains(alleleFullName)){
			return true;
		} else {
			return false;
		}
	}
	
	private boolean isQStrainStored(String qStrainName){
		if (queryNames.contains(qStrainName)){
			return true;
		} else {
			return false;
		}
	}
	
	private boolean isAStrainStored(String aStrainName){
		if (arrayNames.contains(aStrainName)){
			return true;
		} else {
			return false;
		}
	}
	
	private boolean isSMFStored(String smfName){
		if (smfNames.contains(smfName)){
			return true;
		} else {
			return false;
		}
	}
	
	private void storeInteraction(String line, Map<String, Integer> qStrainIDs, Map<String, Integer> aStrainIDs){
		String[] contents = line.split(FIELD_SEPARATER);
		if (contents.length != FIELD_NUMBER){
			log.error("SGA数据格式错误。本行内容" + line);
			return;
		}
		TRInteractionSQL interactionSQL = new TRInteractionSQL();
		
		String qStrain = contents[SGAColums.COL_QUERY_STRAIN.ordinal()];
		String aStrain = contents[SGAColums.COL_ARRAY_STRAIN.ordinal()];
		double expDoubleFitness = Double.parseDouble(contents[SGAColums.COL_EXPECTED_DMF.ordinal()]);
		double ObsDoubleFitness = Double.parseDouble(contents[SGAColums.COL_OBSERVED_DMF.ordinal()]);
		double ObsDoubleFitnessDMF = Double.parseDouble(contents[SGAColums.COL_OBSERVED_DMF_STD.ordinal()]);
		double interactionScore = ObsDoubleFitness - expDoubleFitness;
		double pValue = Double.parseDouble(contents[SGAColums.COL_P_VALUE.ordinal()]);
		double standardDeviation = Double.parseDouble(contents[SGAColums.COL_E_SCORE_STD.ordinal()]);
		String condition = DataFetcher.CONDITION_FAKE;
		
		int qStrainID = -1;
		int aStrainID = -1;
		if (qStrainIDs.containsKey(qStrain)){
			qStrainID = qStrainIDs.get(qStrain);
		}
		
		if (aStrainIDs.containsKey(aStrain)){
			aStrainID = aStrainIDs.get(aStrain);
		}
		
		TRInteraction interaction = new TRInteraction(qStrainID, aStrainID);
		interaction.setEdf(expDoubleFitness);
		interaction.setOdf(ObsDoubleFitness);
		interaction.setOdf_dmf(ObsDoubleFitnessDMF);
		interaction.setInteractionScore(interactionScore);
		interaction.setpValue(pValue);
		interaction.setStandardDeviation(standardDeviation);
		interaction.setCondition(condition);
		
		interactions.add(interaction);
		if (interactions.size() >= (10 * STORE_NUM_MAX)){
			interactionSQL.insert(interactions);
			interactions.clear();
		}
	}
	
	public void fetcheBaseData(int firstLevel, int secondLevel) {
		TRAlleleSQL alleleSQL = new TRAlleleSQL();
		TRStrainSQL qStrainSQL = new TRStrainSQL();
		TRStrainSQL aStrainSQL = new TRStrainSQL();
		TRSmfSQL smfSQL = new TRSmfSQL();
		BufferedReader reader = null;
		long start = System.currentTimeMillis();
		
		switch(firstLevel){
			case 0:
				// Get allele, strain and smf from interaction data source.
				try {
					reader = new BufferedReader(new InputStreamReader(
							new FileInputStream(new File(PREFIX_PATH + DATA_FILEPATH))));
					String line = null;
					switch(secondLevel){
						case 0:
							while ((line = reader.readLine()) != null) {
								storeInteractionAllele(line);
							}
							if (alleles.size() > 0){
								alleleSQL.insert(alleles);
								alleles.clear();
							}
							break;
						case 1:
							while ((line = reader.readLine()) != null) {
								storeStrains(line, STRAIN_MARKER_TYPE_Q);
							}
							if (queryStrains.size() > 0){
								qStrainSQL.insert(queryStrains);
								queryStrains.clear();
							}
							break;
						case 2:
							while ((line = reader.readLine()) != null) {
								storeStrains(line, STRAIN_MARKER_TYPE_A);
							}
							if (arrayStrains.size() > 0){
								aStrainSQL.insert(arrayStrains);
								arrayStrains.clear();
							}
							break;
						case 3: 
							while ((line = reader.readLine()) != null) {
								storeSmf(line);
							}
							if (smfs.size() > 0){
								smfSQL.insert(smfs);
								smfs.clear();
							}
							break;
					
					}
				} catch (FileNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					log.error("文件" + PREFIX_PATH + DATA_FILEPATH + "未找到");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					log.error("读取文件" + DATA_FILEPATH + "失败!");
				} finally {
					if (reader != null){
						try {
							reader.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				break;
				
			case 1:
				// Get strain from query and array correlation data source.
				try {
					reader = new BufferedReader(new InputStreamReader(
							new FileInputStream(new File(PREFIX_PATH + ARRAY_DATA_FILEPATH))));
					String line = null;
					
					switch(secondLevel){
						case 0:
							while ((line = reader.readLine()) != null) {
								storeCorrelationAllele(line, 0);
							}
							if (alleles.size() > 0){
								alleleSQL.insert(alleles);
								alleles.clear();
							}
							break;
						case 1:
							while ((line = reader.readLine()) != null) {
								storeCorrelationAllele(line, 1);
							}
							if (alleles.size() > 0){
								alleleSQL.insert(alleles);
								alleles.clear();
							}
							break;
						case 2:
							while ((line = reader.readLine()) != null) {
								storeStrains(line, STRAIN_MARKER_TYPE_A);
							}
							if (queryStrains.size() > 0){
								qStrainSQL.insert(queryStrains);
								queryStrains.clear();
							}
							break;
						case 3:
							while ((line = reader.readLine()) != null) {
								storeStrains(line, STRAIN_MARKER_TYPE_Q);
							}
							if (arrayStrains.size() > 0){
								aStrainSQL.insert(arrayStrains);
								arrayStrains.clear();
							}
							break;
					
					}
				} catch (FileNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					log.error("文件" + PREFIX_PATH + ARRAY_DATA_FILEPATH + "未找到");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					log.error("读取文件" + ARRAY_DATA_FILEPATH + "失败!");
				} finally {
					if (reader != null){
						try {
							reader.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				break;
		
				
			case 2:
				try {
					reader = new BufferedReader(new InputStreamReader(
							new FileInputStream(new File(PREFIX_PATH + QUERY_DATA_FILEPATH))));
					String line = null;
					
					switch(secondLevel){
					case 0:
						while ((line = reader.readLine()) != null) {
							storeCorrelationAllele(line, 0);
						}
						if (alleles.size() > 0){
							alleleSQL.insert(alleles);
							alleles.clear();
						}
						break;
					case 1:
						while ((line = reader.readLine()) != null) {
							storeCorrelationAllele(line, 1);
						}
						if (alleles.size() > 0){
							alleleSQL.insert(alleles);
							alleles.clear();
						}
						break;
					case 2:
						while ((line = reader.readLine()) != null) {
							storeStrains(line, STRAIN_MARKER_TYPE_A);
						}
						if (queryStrains.size() > 0){
							qStrainSQL.insert(queryStrains);
							queryStrains.clear();
						}
						break;
					case 3:
						while ((line = reader.readLine()) != null) {
							storeStrains(line, STRAIN_MARKER_TYPE_Q);
						}
						if (arrayStrains.size() > 0){
							aStrainSQL.insert(arrayStrains);
							arrayStrains.clear();
						}
						break;
				
				}
				} catch (FileNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					log.error("文件" + PREFIX_PATH + QUERY_DATA_FILEPATH + "未找到");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					log.error("读取文件" + QUERY_DATA_FILEPATH + "失败!");
				} finally {
					if (reader != null){
						try {
							reader.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				break;
			default:
				break;
		}
		long end = System.currentTimeMillis();
		log.info("共用" + (end - start) + "ms");
	}
	
	public void fetchRelationData(){
		BufferedReader reader = null;
		TRStrainSQL strainSQL = new TRStrainSQL();
		Map<String, Integer> qStrainIDs = strainSQL.getStrainIDs(STRAIN_MARKER_TYPE_Q);
		Map<String, Integer> aStrainIDs = strainSQL.getStrainIDs(STRAIN_MARKER_TYPE_A);
		TRInteractionSQL interactionSQL = new TRInteractionSQL();
		long start = System.currentTimeMillis();
		try {
			reader = new BufferedReader(new InputStreamReader(
					new FileInputStream(new File(PREFIX_PATH + DATA_FILEPATH))));
			String line = null;
			
			while ((line = reader.readLine()) != null) {
				storeInteraction(line, qStrainIDs, aStrainIDs);
			}
			if (interactions.size() > 0){
				interactionSQL.insert(interactions);
				interactions.clear();
			}
		}catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			log.error("文件" + PREFIX_PATH + DATA_FILEPATH + "未找到");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			log.error("读取文件" + DATA_FILEPATH + "失败!");
		} finally {
			if (reader != null){
				try {
					reader.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		long end = System.currentTimeMillis();
		log.info("共用" + (end - start) + "ms");
	}
	
	public static void main(String[] args) throws URISyntaxException{
//		YeastGeneFether yeastGeneFether = new YeastGeneFether();
//		yeastGeneFether.fetchYeastData();
		DataFetcher fetcher = new DataFetcher();
		for (int i = 0; i < 3; i++){
			for (int j = 0; j < 4; j++){
				fetcher.fetcheBaseData(i, j);
			}
		}
		fetcher.fetchRelationData();
		CorrelationFetcher cFetcher = new CorrelationFetcher();
		cFetcher.getCorrelationData();
	}
}
